#include "JsonMap.h"

namespace
{
#define PUT_FUNCTION(key, value, putListBehavior) d->putPreprocess(key, putListBehavior); \
    for (const auto &v: value) {d->value[key].append(Json::Value(v)); }
#define CHECK_PARSER if(!d->isParserOk){throw sh::JsonMapFormatException(d->error);}
}

class JsonMapData
{
private:
    friend class sh::JsonMap;
    bool isParserOk{false};
    Json::Value value;
    JSONCPP_STRING error;
private:
    void putPreprocess(const std::string &key, sh::PutListBehavior putListBehavior);
};
void JsonMapData::putPreprocess(const std::string &key, sh::PutListBehavior putListBehavior)
{
    if (!value.isMember(key))
    {
        return;
    }
    if (!value[key].isArray())
        value.removeMember(key);
    else if (sh::PutListBehavior::Cover == putListBehavior)
        value.removeMember(key);
}

sh::JsonMap::JsonMap()
    : d(std::unique_ptr<JsonMapData>(new JsonMapData()))
{
    d = std::unique_ptr<JsonMapData>(new JsonMapData());
}

sh::JsonMap::JsonMap(const std::string &content)
{
    d = std::unique_ptr<JsonMapData>(new JsonMapData());
    parser(content);
}
sh::JsonMap::JsonMap(const sh::JsonMap &jsonMap)
    : d(std::unique_ptr<JsonMapData>(new JsonMapData()))
{
    d->value = jsonMap.d->value;
    d->isParserOk = true;
}
sh::JsonMap::JsonMap(const Json::Value &value)
    : d(std::unique_ptr<JsonMapData>(new JsonMapData()))
{
    d->value = value;
    d->isParserOk = true;
}
sh::JsonMap::~JsonMap() = default;

bool sh::JsonMap::parser(const std::string &content)
{
    Json::CharReaderBuilder builder;
    auto reader = std::unique_ptr<Json::CharReader>(builder.newCharReader());
    d->isParserOk = reader->parse(content.c_str(), content.c_str() + content.length(), &d->value, &d->error);
    return d->isParserOk;
}
std::string sh::JsonMap::formatJson() const
{
    CHECK_PARSER
    return d->value.toStyledString();
}
bool sh::JsonMap::isParserOk() const
{
    return d->isParserOk;
}
std::string sh::JsonMap::getParserErrorMessage() const
{
    return d->error;
}
bool sh::JsonMap::containKey(const std::string &key) const
{
    return d->value.isMember(key);
}
sh::JsonMap sh::JsonMap::get(const std::string &key) const
{
    CHECK_PARSER
    if (!d->value.isMember(key))
    {
        throw sh::JsonMapNotExistException("key [" + key + "] is not exist");
    }
    return sh::JsonMap(d->value[key]);
}
sh::JsonMap sh::JsonMap::remove(const std::string &key)
{
    if (d->value.isMember(key))
    {
        d->value.removeMember(key);
    }
    return *this;
}
std::string sh::JsonMap::toString() const
{
    CHECK_PARSER
    if (!d->value.isString())
    {
        throw sh::JsonMapTypeException("type is not string");
    }
    return d->value.asString();
}
int32_t sh::JsonMap::toInt() const
{
    CHECK_PARSER
    if (!d->value.isInt())
    {
        throw sh::JsonMapTypeException("type is not int");
    }
    return d->value.asInt();
}
uint32_t sh::JsonMap::toUInt() const
{
    CHECK_PARSER
    if (!d->value.isUInt())
    {
        throw sh::JsonMapTypeException("type is not unsigned int");
    }
    return d->value.asUInt();
}
int64_t sh::JsonMap::toLong() const
{
    CHECK_PARSER
    if (!d->value.isInt64())
    {
        throw sh::JsonMapTypeException("type is not long int");
    }
    return d->value.asInt64();
}
uint64_t sh::JsonMap::toULong() const
{
    CHECK_PARSER
    if (!d->value.isUInt64())
    {
        throw sh::JsonMapTypeException("type is not unsigned long int");
    }
    return d->value.asUInt64();
}
double sh::JsonMap::toDouble() const
{
    CHECK_PARSER
    if (!d->value.isDouble())
    {
        throw sh::JsonMapTypeException("type is not double");
    }
    return d->value.asDouble();
}
float sh::JsonMap::toFloat() const
{
    CHECK_PARSER
    if (!d->value.isDouble())
    {
        throw sh::JsonMapTypeException("type is not float");
    }
    return d->value.asFloat();
}
bool sh::JsonMap::toBool() const
{
    CHECK_PARSER
    if (!d->value.isBool())
    {
        throw sh::JsonMapTypeException("type is not bool");
    }
    return d->value.asBool();
}
sh::JsonMap sh::JsonMap::put(const std::string &key, const sh::JsonMap &value)
{
    CHECK_PARSER
    d->value[key] = value.d->value;
    return *this;
}
sh::JsonMap sh::JsonMap::put(const std::string &key, const std::string &value)
{
    CHECK_PARSER
    d->value[key] = value;
    return *this;
}
sh::JsonMap sh::JsonMap::put(const std::string &key, int32_t value)
{
    CHECK_PARSER
    d->value[key] = value;
    return *this;
}
sh::JsonMap sh::JsonMap::put(const std::string &key, uint32_t value)
{
    CHECK_PARSER
    d->value[key] = value;
    return *this;
}
sh::JsonMap sh::JsonMap::put(const std::string &key, int64_t value)
{
    CHECK_PARSER
    d->value[key] = value;
    return *this;
}
sh::JsonMap sh::JsonMap::put(const std::string &key, uint64_t value)
{
    CHECK_PARSER
    d->value[key] = value;
    return *this;
}
sh::JsonMap sh::JsonMap::put(const std::string &key, double value)
{
    CHECK_PARSER
    d->value[key] = value;
    return *this;
}
sh::JsonMap
sh::JsonMap::put(const std::string &key, const std::list<std::string> &value, PutListBehavior putListBehavior)
{
    CHECK_PARSER
    PUT_FUNCTION(key, value, putListBehavior)
    return *this;
}
sh::JsonMap sh::JsonMap::put(const std::string &key, const std::list<int32_t> &value, PutListBehavior putListBehavior)
{
    CHECK_PARSER
    PUT_FUNCTION(key, value, putListBehavior)
    return *this;
}
sh::JsonMap sh::JsonMap::put(const std::string &key, const std::list<uint32_t> &value, PutListBehavior putListBehavior)
{
    CHECK_PARSER
    PUT_FUNCTION(key, value, putListBehavior)
    return *this;
}
sh::JsonMap sh::JsonMap::put(const std::string &key, const std::list<int64_t> &value, PutListBehavior putListBehavior)
{
    CHECK_PARSER
    PUT_FUNCTION(key, value, putListBehavior)
    return *this;
}
sh::JsonMap sh::JsonMap::put(const std::string &key, const std::list<uint64_t> &value, PutListBehavior putListBehavior)
{
    CHECK_PARSER
    PUT_FUNCTION(key, value, putListBehavior)
    return *this;
}
sh::JsonMap sh::JsonMap::put(const std::string &key, const std::list<double> &value, PutListBehavior putListBehavior)
{
    CHECK_PARSER
    PUT_FUNCTION(key, value, putListBehavior)
    return *this;
}
std::vector<std::string> sh::JsonMap::keys() const
{
    CHECK_PARSER
    return d->value.getMemberNames();
}
uint32_t sh::JsonMap::size() const
{
    CHECK_PARSER
    return d->value.getMemberNames().size();
}
Json::Value sh::JsonMap::getJsonValue() const
{
    CHECK_PARSER
    return d->value;
}

sh::JsonMapException::JsonMapException() = default;

sh::JsonMapException::JsonMapException(const std::string &error)
    : message(std::move(error))
{
}
std::string sh::JsonMapException::error() const
{
    return message;
}
sh::JsonMapNotExistException::JsonMapNotExistException(const std::string &error)
    : JsonMapException(error)
{

}
sh::JsonMapTypeException::JsonMapTypeException(const std::string &error)
    : JsonMapException(error)
{

}
sh::JsonMapFormatException::JsonMapFormatException(const std::string &error)
    : JsonMapException(error)
{

}
